import java.math.*;
import java.util.*;

public class Circuit{

    Player p;

    HashMap<Integer, InputGate> inputGates;    
    HashMap<Integer, ConstGate> constGates;    
    HashMap<Integer, AddGate> addGates;    
    HashMap<Integer, MultGate> multGates;
    HashMap<Integer, OutputGate> outputGates;

    int nextMultGate;

    public Circuit(Player p){
        this.p = p;
        inputGates = new HashMap<Integer, InputGate>();
        constGates = new HashMap<Integer, ConstGate>();
        addGates = new HashMap<Integer, AddGate>();
        multGates = new HashMap<Integer, MultGate>();
        outputGates = new HashMap<Integer, OutputGate>();
        nextMultGate = 1;
    }

    public void addConstGate(ConstGate g){
        constGates.put(g.getID(), g.copy());
    }

    public void addInputGate(InputGate g){
        inputGates.put(g.getID(), g.copy());
    }

    public void addAddGate(AddGate g){
        addGates.put(g.getID(), g.copy());
    }

    public void addMultGate(MultGate g){
        multGates.put(g.getID(), g.copy());
    }

    public void addOutputGate(OutputGate g){
        outputGates.put(g.getID(), g.copy());
    }

    public void initInputGate(BigInteger share, int ID){
        inputGates.get(ID).setShare(share);        
    }

    public void initMultGate(BigInteger share){
        multGates.get(nextMultGate).setShare(share);
        nextMultGate++;
    }   

    public void initOutputGate(BigInteger share, int ID){
        outputGates.get(ID).setShare(share);
        p.cc.continueToTest(p.getOldID());
    }

    public void test(){
        System.out.println("============== Player "+p.getOldID()+" TESTING CIRCUIT ===============");
        System.out.println("");
        BigInteger output = outputGates.get(p.getOldID()).share;
        if(Utilities.mode == 2 || Utilities.mode == 3){
            if(output.equals(BigInteger.valueOf(6))){
                System.out.println("TEST SUCCESSFUL! - output was 6");
            }
            else{
                System.out.println("TEST FAILED! :( - output was " + output +" when it should have been 6.");
            }
            if(p.getID() == Utilities.n_prime){
                p.main.time(false);
            }
            return;
        }
        //Else mode is 1, and we can continue as usual...
        BigInteger trueResult = BigInteger.valueOf(40).mod(Utilities.F_big);
        if(output.equals(trueResult))
            System.out.println("TEST SUCCESSFUL! - output was "+output);
        else
            System.out.println("TEST FAILED! :( - output was " + output +" when it should have been "+trueResult+".");
        System.out.println("");
        
        if(p.getID() == Utilities.n_prime){
            p.main.time(false);
        }
    }

    public void evalAddGate(int ID){       
        for(Integer i: addGates.keySet()){
            if(i == ID){
            //can do this since we know everything before this has its
            //share set.
                AddGate g = addGates.get(i);
                Gate left = removeGate(g.leftType, g.leftID);                
                Gate right = removeGate(g.rightType, g.rightID);
                BigInteger share = left.getShare().add(right.getShare()).mod(Utilities.F_big);
                g.setShare(share);
            }
        }
    }

    /**
     * removes a gate from the circuit based on the type of gate and
     * the given ID
     */
    public Gate removeGate(int type, int ID){
        switch(type){
        case Utilities.INPUTGATE:
            return inputGates.get(ID);
        case Utilities.CONSTGATE:
            return constGates.get(ID);
        case Utilities.ADDGATE:
            return addGates.get(ID);
        case Utilities.MULTGATE:       
            return multGates.get(ID);
        }

        return null; //should never happen.. error if so
    }

    /**
     * removes a gate from the circuit based on the type of gate and
     * the given ID
     */
    public Gate getGate(int type, int ID){
        switch(type){
        case Utilities.INPUTGATE:
            return inputGates.get(ID);
        case Utilities.CONSTGATE:
            return constGates.get(ID);
        case Utilities.ADDGATE:
            return addGates.get(ID);
        case Utilities.MULTGATE:
            return multGates.get(ID);
        }

        return null; //should never happen.. error if so
    }

}